package com.ruyuan.commodity.common.mamager.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.ruyuan.commodity.common.mapper.GoodsMapper;
import com.ruyuan.commodity.common.mapper.GoodsSkuMapper;
import com.ruyuan.commodity.model.goods.vo.GoodsQuantityVO;
import com.ruyuan.commodity.open.api.model.goods.enums.QuantityType;
import com.ruyuan.commodity.utils.StockCacheKeyUtil;
import com.ruyuan.commodity.common.mamager.GoodsQuantityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 商品库存接口
 */
@Service
public class GoodsQuantityManagerImpl implements GoodsQuantityManager {


    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 商品mapper组件
     */
    @Autowired
    private GoodsMapper goodsMapper;
    /**
     * 商品SKU的mapper组件
     */
    @Autowired
    private GoodsSkuMapper goodsSkuMapper;
    /**
     * 商品
     */
    @Autowired
    public RedisTemplate<String, String> redisTemplate;

    private static RedisScript<Boolean> script = null;

    private static RedisScript<Boolean> getRedisScript() {

        if (script != null) {
            return script;
        }

        ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("sku_quantity.lua"));
        String str = null;
        try {
            str = scriptSource.getScriptAsString();
        } catch (IOException e) {
            e.printStackTrace();
        }

        script = RedisScript.of(str, Boolean.class);
        return script;
    }

    /**
     * 库存更新接口
     * @param goodsQuantityList 要更新的库存vo List
     * @return 如果更新成功返回真，否则返回假
     */
    @Override
    public Boolean updateSkuQuantity(List<GoodsQuantityVO> goodsQuantityList) {

        List<Long> skuIdList = new ArrayList<>();
        List<Long> goodsIdList = new ArrayList<>();

        List<String> keys = new ArrayList<>();
        List<String> values = new ArrayList<>();

        for (GoodsQuantityVO quantity : goodsQuantityList) {

            Assert.notNull(quantity.getGoodsId(), "goods id must not be null");
            Assert.notNull(quantity.getSkuId(), "sku id must not be null");
            Assert.notNull(quantity.getQuantity(), "quantity id must not be null");
            Assert.notNull(quantity.getQuantityType(), "Type must not be null");


            // sku库存
            if (QuantityType.enable.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.skuEnableKey(quantity.getSkuId()));
            } else if (QuantityType.actual.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.skuActualKey(quantity.getSkuId()));
            }
            values.add("" + quantity.getQuantity());

            //goods库存key
            if (QuantityType.enable.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.goodsEnableKey(quantity.getGoodsId()));
            } else if (QuantityType.actual.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.goodsActualKey(quantity.getGoodsId()));
            }
            values.add("" + quantity.getQuantity());


            skuIdList.add(quantity.getSkuId());
            goodsIdList.add(quantity.getGoodsId());
        }

        // 获取更新库存的脚本
        RedisScript<Boolean> redisScript = getRedisScript();
        // 执行更新库存的脚本
        Boolean result = redisTemplate.execute(redisScript, keys, values.toArray());

        logger.info("更新库存：[{}] 更新结果:[{}]", goodsQuantityList.toString(), result);

        //如果lua脚本执行成功则记录缓冲区
        if (!ObjectUtils.isEmpty(result) && result) {
            syncDataBase(skuIdList, goodsIdList);
        }

        return result;
    }

    /**
     * 为某个sku 填充库存cache<br/>
     * 库存数量由数据库中获取<br/>
     * 一般用于缓存被击穿的情况
     * @param skuId skuId
     * @return 可用库存和实际库存
     */
    @Override
    public Map<String, Integer> fillCacheFromDb(Long skuId) {
        Map<String, Integer> map = this.goodsSkuMapper.queryQuantity(skuId);
        Integer enableNum = map.get("enable_quantity");
        Integer actualNum = map.get("quantity");

        redisTemplate.opsForValue().set(StockCacheKeyUtil.skuActualKey(skuId), String.valueOf(actualNum));
        redisTemplate.opsForValue().set(StockCacheKeyUtil.skuEnableKey(skuId), String.valueOf(enableNum));
        return map;
    }


    /**
     * 同步数据库中的库存
     *
     * @param skuIdList   需要同步的skuId
     * @param goodsIdList 需要同步的goodsId
     */
    private void syncDataBase(List<Long> skuIdList, List<Long> goodsIdList) {
        //批量获取sku的库存
        List<String> skuKeys = StockCacheKeyUtil.skuKeys(skuIdList);
        List<String> skuQuantityList = redisTemplate.opsForValue().multiGet(skuKeys);

        if (CollectionUtils.isEmpty(skuQuantityList)) {
            return;
        }

        int i = 0;

        //形成批量更新sku的list
        for (Long skuId : skuIdList) {

            new UpdateChainWrapper<>(goodsSkuMapper)
                    // 修改可用库存
                    .set("enable_quantity", skuQuantityList.get(i))
                    // 修改实际库存
                    .set("quantity", skuQuantityList.get(i + 1))
                    // 根据skuId修改
                    .eq("sku_id", skuId)
                    .update();

            i = i + 2;
        }

        //批量获取商品的库存
        List<String> goodsKeys = createGoodsKeys(goodsIdList);
        List<String> goodsQuantityList = redisTemplate.opsForValue().multiGet(goodsKeys);

        if (CollectionUtils.isEmpty(goodsQuantityList)) {
            return;
        }

        i = 0;

        //形成批量更新goods的list
        for (Long goodsId : goodsIdList) {

            new UpdateChainWrapper<>(goodsMapper)
                    // 修改可用库存
                    .set("enable_quantity", goodsQuantityList.get(i))
                    // 修改实际库存
                    .set("quantity", goodsQuantityList.get(i + 1))
                    // 根据商品id修改
                    .eq("goods_id", goodsId)
                    .update();
            i = i + 2;
        }

    }

    /**
     * 生成批量获取goods库存的keys
     *
     * @param goodsIdList 商品id集合
     * @return 商品实际库存和可用库存的redis key
     */
    private List<String> createGoodsKeys(List<Long> goodsIdList) {
        List<String> keys = new ArrayList<>();
        for (Long goodsId : goodsIdList) {
            //商品可用库存redis key
            keys.add(StockCacheKeyUtil.goodsEnableKey(goodsId));
            //商品实际库存redis key
            keys.add(StockCacheKeyUtil.goodsActualKey(goodsId));
        }
        return keys;
    }

}
